/**
 * Copyright (c) 2019-2029, wwww.kiwipeach.cn (liuburu@qq.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * https://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package cn.kiwipeach.testcase.jwt;

import cn.kiwipeach.blog.domain.AccessTokenVO;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import open.kiwipeach.autoconfigure.shiro.token.AccessToken;
import open.kiwipeach.autoconfigure.web.utils.JwtTokenUtil;
import org.junit.jupiter.api.Test;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * JWT的组成：
 * 头部（header）：
 * { 'typ': 'JWT', 'alg': 'HS256' } ==(加密)==>  eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
 * 负载（payload）：
 * 标准中注册的声明
 * iss: jwt签发者
 * sub: jwt所面向的用户
 * aud: 接收jwt的一方
 * exp: jwt的过期时间，这个过期时间必须要大于签发时间
 * nbf: 定义在什么时间之前，该jwt都是不可用的.
 * iat: jwt的签发时间
 * jti: jwt的唯一身份标识，主要用来作为一次性token,从而回避重放攻击。
 * 公共的声明
 * <p>
 * 签证（signature）：
 */
@Slf4j
public class JwtTestCase {

    @Test
    public void JWT对称机密() {
        try {
            Algorithm algorithm = Algorithm.HMAC256("secret");

            Map<String, Object> headerClaims = new HashMap();
            headerClaims.put("owner", "Java开源博客版权所有");

            Map<String, Object> claims = new HashMap<String, Object>();//创建payload的私有声明（根据特定的业务需要添加，如果要拿这个做验证，一般是需要和jwt的接收方提前沟通好验证方式的）
            claims.put("uid", "DSSFAWDWADAS...");
            claims.put("user_name", "admin");
            claims.put("nick_name", "DASDA121");

            Date nowDate = new Date();
            long expMillis = nowDate.getTime() + 60 * 1000 * 5; //5分钟
            long notBfMillis = nowDate.getTime() + 60 * 1000; //30秒
            Date expire = new Date(expMillis);
            Date nbf = new Date(notBfMillis);
            /**
             *   参考：com.auth0.jwt.impl.PublicClaims
             *  * iss: jwt签发者
             *  * sub: jwt所面向的用户
             *  * aud: 接收jwt的一方
             *  * exp: jwt的过期时间，这个过期时间必须要大于签发时间
             *  * nbf: 定义在什么时间之前，该jwt都是不可用的.
             *  * iat: jwt的签发时间
             *  * jti: jwt的唯一身份标识，主要用来作为一次性token,从而回避重放攻击。
             */
            String token = JWT.create()
                    .withHeader(headerClaims)
                    .withIssuer("nice-blog-sys")             //jwt签发者
                    .withSubject("blog common user.")            //jwt所面向的用户
                    .withExpiresAt(expire)                 //jwt的过期时间，这个过期时间必须要大于签发时间
                    .withNotBefore(nbf)                 //定义在什么时间之前，该jwt都是不可用的.
                    .withIssuedAt(nowDate)                  //jwt的签发时间
                    .withJWTId(UUID.randomUUID().toString())
                    .withClaim("user", claims)
                    .sign(algorithm);
            log.info("签发token值:{}", token);
        } catch (JWTCreationException exception) {
        }
    }

    @Test
    public void JWT加密验证() {
        String token = "eyJvd25lciI6Im5pY2UtYmxvZy1zeXMiLCJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJsaXVidXJ1QHFxLmNvbSIsIm5iZiI6MTU4Njc1NDk4NCwiaXNzIjoia2l3aXBlYWNoIiwiZXhwIjoxNTg2NzU1MjI0LCJpYXQiOjE1ODY3NTQ5MjQsInVzZXIiOnsidWlkIjoiRFNTRkFXRFdBREFTLi4uIiwidXNlcl9uYW1lIjoiYWRtaW4iLCJuaWNrX25hbWUiOiJEQVNEQTEyMSJ9fQ.TxnrilE1xM4cBzffXH1iJMMrIozMFfG4SkdU2k_UO_0";
        try {
            Algorithm algorithm = Algorithm.HMAC256("secret");
            JWTVerifier verifier = JWT.require(algorithm)
                    .withIssuer("kiwipeach")
                    .build(); //Reusable verifier instance
            DecodedJWT jwt = verifier.verify(token);
            log.info("获取token签发者:{}", jwt.getIssuer());
        } catch (JWTVerificationException exception) {
            //Invalid signature/claims
            log.info("token验证失败:{}", exception.getMessage());
        }
    }

    @Test
    public void JWT解密() {
        String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJldmVyeW9uZSIsIm5iZiI6MTU4NzE5NjM1NSwiaXNzIjoia2l3aXBlYWNoQHFxLmNvbSIsImV4cCI6MTU4NzE5Njk1NSwiVXNlci1Ub2tlbiI6eyJuaWNrTmFtZSI6IuWcqOS5juS9oOeahOWcqOS5jiIsImhlYWRVcmwiOiJodHRwczovL2dpdGVlLmNvbS91cGxvYWRzLzc4LzEzODc1Nzhfa2FrYWx1b3RlNDQ0LnBuZyIsInVzZXJOYW1lIjoia2l3aXBlYWNoIiwidXNlcklkIjoiMSJ9LCJpYXQiOjE1ODcxOTYzNTUsImp0aSI6Ijc0ZjI4ZWYxLTllZDAtNDZiZS04ZWUxLWNhOWFjZmI3N2IzYyJ9.fvHarNiC5SBrMs8sOraPha_O80JmCPHvhjl5fYdjbQ0eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJldmVyeW9uZSIsIm5iZiI6MTU4NzE5NjM1NSwiaXNzIjoia2l3aXBlYWNoQHFxLmNvbSIsImV4cCI6MTU4NzE5Njk1NSwiVXNlci1Ub2tlbiI6eyJuaWNrTmFtZSI6IuWcqOS5juS9oOeahOWcqOS5jiIsImhlYWRVcmwiOiJodHRwczovL2dpdGVlLmNvbS91cGxvYWRzLzc4LzEzODc1Nzhfa2FrYWx1b3RlNDQ0LnBuZyIsInVzZXJOYW1lIjoia2l3aXBlYWNoIiwidXNlcklkIjoiMSJ9LCJpYXQiOjE1ODcxOTYzNTUsImp0aSI6Ijc0ZjI4ZWYxLTllZDAtNDZiZS04ZWUxLWNhOWFjZmI3N2IzYyJ9.fvHarNiC5SBrMs8sOraPha_O80JmCPHvhjl5fYdjbQ0";
        try {
            DecodedJWT jwt = JWT.decode(token);
            log.info("获取解密:{},{}", jwt.getIssuer(), jwt.getClaim("user"));
        } catch (JWTDecodeException exception) {
            //Invalid token
            log.info("token解密失败:{}", exception.getMessage());
        }
    }

    @Test
    public void 加密() {

        AccessToken accessToken = new AccessToken(UUID.randomUUID().toString(), "kiwipeach", "浪漫人", "http://www.kiwipeach.cn/images/head.png", "system");
        accessToken.setId("1001");
        AccessTokenVO accessTokenVO = JwtTokenUtil.signToken(accessToken);
        String token = accessTokenVO.getAccessToken();
        if (JwtTokenUtil.verify(token)) {
            AccessToken decode = JwtTokenUtil.decode(token);
            log.warn("解析结果：{}", decode);
        }

    }

}
